package com.supplychain.scm.core.framework.util;



import cn.hutool.core.map.MapUtil;
import com.alibaba.fastjson.JSON;
import com.supplychain.scm.core.framework.annotation.Default;
import com.supplychain.scm.core.framework.chain.base.AbstractChain;
import com.supplychain.scm.core.framework.strategy.AbstractStrategy;
import com.google.common.collect.Lists;
import com.google.common.collect.Maps;
import com.supplychain.scm.core.basis.constant.CommonConstants;
import com.supplychain.scm.core.common.util.SpringContextHolder;
import lombok.SneakyThrows;
import lombok.experimental.UtilityClass;
import org.springframework.core.annotation.Order;

import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Objects;

/**
 * @author liyang
 * @version 1.0.0
 * @ClassName FraneworkUtils
 * @Description 框架工具类
 * @createTime 17:17:00 2022/10/14
 */
@UtilityClass
public class FrameworkUtils {

    /**
     * 获取策略实例
     *
     * @param clazz 业务类型抽象Class
     * @param args 需要匹配判断的入参
     * @param <T>
     * @return
     */
    public <T> T strategyInstance(Class<T> clazz, Object... args) {
        if(!AbstractStrategy.class.isAssignableFrom(clazz)){
            throw new UnsupportedOperationException(String.format("%s.class not a subclass of AbstractStrategy! args: %s",clazz.getName(), JSON.toJSONString(args)));
        }
        Map<String, T> beansMap = SpringContextHolder.getBeans(clazz);
        if (Objects.isNull(beansMap) || beansMap.size() == CommonConstants.ZERO) {
            throw new UnsupportedOperationException(String.format("%s.class instance does not exist! args: %s",clazz.getName(), JSON.toJSONString(args)));
        }
        T t = null;
        List<T> defaults = Lists.newArrayList();
        Iterator<Map.Entry<String, T>> entries = beansMap.entrySet().iterator();
        while (entries.hasNext()) {
            Map.Entry<String, T> entry = entries.next();
            AbstractStrategy abstractStrategy = (AbstractStrategy) entry.getValue();
            if(entry.getValue().getClass().isAnnotationPresent(Default.class)){
                defaults.add(entry.getValue());
            }
            if(abstractStrategy.support(args)){
                t = entry.getValue();
                break;
            }
        }
        t = Objects.isNull(t) ? defaults.stream().findAny().orElse(null) : t;
        if (Objects.isNull(t)) {
            throw new UnsupportedOperationException(String.format("%s.class instance does not exist! args: %s",clazz.getName(), JSON.toJSONString(args)));
        }
        return t;
    }

    /**
     * 获取职责链实例
     * Map<排序,List<实现链>>
     * @param clazz 业务类型抽象Class
     * @param <T>
     * @return
     */
    public <T> Map<Integer,List<T>> chainInstance(Class<T> clazz) {
        if(!AbstractChain.class.isAssignableFrom(clazz)){
            throw new UnsupportedOperationException(String.format("%s.class not a subclass of AbstractStrategy",clazz.getName()));
        }
        Map<String,T> beanMap = SpringContextHolder.getBeans(clazz);
        if (MapUtil.isEmpty(beanMap)) {
            throw new UnsupportedOperationException(String.format("%s.class instance does not exist",clazz.getName()));
        }
        Map<Integer,List<T>> sortMap = Maps.newTreeMap();
        for (T value : beanMap.values()) {
            Class<?> clazzValue = value.getClass();
            int order = -1;
            if(clazzValue.isAnnotationPresent(Order.class)){
                Order annotation = clazzValue.getAnnotation(Order.class);
                order = annotation.value();
            }
            if(!sortMap.containsKey(order)){
                sortMap.put(order,Lists.newArrayList());
            }
            sortMap.get(order).add(value);
        }
        return sortMap;
    }

    /**
     * 获取职责链实例
     * @param className 职责链实现全名称
     * @param <T>
     * @return
     */
    @SneakyThrows
    public <T> T chaninInstance(String className,Class<T> tClass) {
        Class<?> clazz = Class.forName(className);
        if(!tClass.isAssignableFrom(clazz)) {
            throw new UnsupportedOperationException(String.format("%s.class not a subclass of AbstractStrategy",clazz.getName()));
        }
        Object object = SpringContextHolder.getBean(clazz);
        return ((T) object);
    }

    /**
     *
     * @param clazz
     * @param args
     * @param <T>
     * @return
     */
    public <T> T chaninInstance(Class<T> clazz, Object... args) {
        if(!AbstractChain.class.isAssignableFrom(clazz)){
            throw new UnsupportedOperationException(String.format("%s.class not a subclass of AbstractChain",clazz.getName()));
        }
        Map<String, T> beansMap = SpringContextHolder.getBeans(clazz);
        if (Objects.isNull(beansMap) || beansMap.size() == CommonConstants.ZERO) {
            throw new UnsupportedOperationException(String.format("%s.class instance does not exist",clazz.getName()));
        }
        T t = null;
        Iterator<Map.Entry<String, T>> entries = beansMap.entrySet().iterator();
        while (entries.hasNext()) {
            Map.Entry<String, T> entry = entries.next();
            AbstractChain abstractChain = (AbstractChain) entry.getValue();
            if(abstractChain.support(args)){
                t = entry.getValue();
                break;
            }
        }
        if (Objects.isNull(t)) {
            throw new UnsupportedOperationException(String.format("%s.class instance does not exist",clazz.getName()));
        }
        return t;
    }
}
